package com.djx.comm.util;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.sql.Timestamp;
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * 日期帮助类
 *
 * @author dingjunxiong
 * @date 2020/11/18
 */
public class DateUtil {
    private static final Logger logger = LoggerFactory.getLogger(DateUtil.class);
    /**
     * 默认日期格式
     */
    public static final String DEFAULT_FORMAT = "yyyy-MM-dd";
    public static final String PATTERN_GRACE_SIMPLE = "yyyy/MM/dd";
    public static final String YYYYMMDDHHMMSSS = "yyyyMMddHHmmsss";
    public static final String PATTERN_CLASSICAL = "yyyy-MM-dd HH:mm:ss";

    private static Pattern DATE_PATTERN = Pattern.compile("^((\\d{2}(([02468][048])|([13579][26]))[\\-\\/\\s]?((((0?[13578])|(1[02]))[\\-\\/\\s]?((0?[1-9])|([1-2][0-9])|(3[01])))|(((0?[469])|(11))[\\-\\/\\s]?((0?[1-9])|([1-2][0-9])|(30)))|(0?2[\\-\\/\\s]?((0?[1-9])|([1-2][0-9])))))|(\\d{2}(([02468][1235679])|([13579][01345789]))[\\-\\/\\s]?((((0?[13578])|(1[02]))[\\-\\/\\s]?((0?[1-9])|([1-2][0-9])|(3[01])))|(((0?[469])|(11))[\\-\\/\\s]?((0?[1-9])|([1-2][0-9])|(30)))|(0?2[\\-\\/\\s]?((0?[1-9])|(1[0-9])|(2[0-8]))))))(\\s(((0?[0-9])|([1-2][0-3]))\\:([0-5]?[0-9])((\\s)|(\\:([0-5]?[0-9])))))?$");
    /**
     * 时间：年正则:2019, 2019-, 2019-2020
     */
    private static Pattern DATE_PATTERN1 = Pattern.compile("^\\d{4}\\-*(\\d{4})*$");
    /**
     * 时间：月正则:201901, 201901-, 201901-201912
     */
    private static Pattern DATE_PATTERN2 = Pattern.compile("^\\d{4}\\d{2}\\-*(\\d{4}\\d{2})*$");
    /**
     * 时间：日正则20190101, 20190101-, 20190111-20191223
     */
    private static Pattern DATE_PATTERN3 = Pattern.compile("^\\d{4}\\d{2}\\d{2}\\-*(\\d{4}\\d{2}\\d{2})*$");
    /**
     * 时间：季度正则 eg：2021a，2013D
     */
    private static Pattern DATE_PATTERN4 = Pattern.compile("^[0-9]{4}[a-dA-D]{1}$");
    /**
     * 根据日期判断星座，dayArr为各个星座在月份中的分隔日期
     */
    private static final int[] dayArr = new int[] { 20, 19, 21, 20, 21, 22, 23, 23, 23, 24, 23, 22 };
    /**
     * 星座数组
     */
    private static final String[] constellationArr = new String[] { "摩羯座", "水瓶座", "双鱼座",
            "白羊座", "金牛座", "双子座", "巨蟹座", "狮子座", "处女座", "天秤座", "天蝎座", "射手座", "摩羯座" };

    /**
     * 根据默认格式将指定字符串解析成日期
     *
     * @param str
     *            指定字符串
     * @return 返回解析后的日期
     */
    public static Date parse(String str) {
        return parse(str, PATTERN_CLASSICAL);
    }

    /**
     * 根据指定格式将指定字符串解析成日期
     *
     * @param str
     *            指定日期
     * @param pattern
     *            指定格式
     * @return 返回解析后的日期
     */
    public static Date parse(String str, String pattern) {
        SimpleDateFormat sdf = new SimpleDateFormat(pattern);
        try {
            return sdf.parse(str);
        } catch (ParseException e) {
            return null;
        }
    }

    /**
     * 根据默认格式将日期转格式化成字符串
     *
     * @param date
     *            指定日期
     * @return 返回格式化后的字符串
     */
    public static String format(Date date) {
        return formatDate(date, PATTERN_CLASSICAL);
    }

    /**
     * 格式化日期
     * @param date 日期对象
     * @return String 日期字符串
     */
    public static String formatDate(Date date, String format){
        if(StringUtils.isBlank(format)){
            format = DEFAULT_FORMAT;
        }
        SimpleDateFormat f = new SimpleDateFormat(format);
        return f.format(date);
    }

    /**
     * 获取当年的第一天
     * @return
     */
    public static Date getCurrYearFirst(){
        Calendar currCal=Calendar.getInstance();
        int currentYear = currCal.get(Calendar.YEAR);
        return getYearFirst(currentYear);
    }

    /**
     * 获取当年的最后一天
     * @return
     */
    public static Date getCurrYearLast(){
        Calendar currCal=Calendar.getInstance();
        int currentYear = currCal.get(Calendar.YEAR);
        return getYearLast(currentYear);
    }

    /**
     * 获取某年第一天日期
     * @param year 年份
     * @return Date
     */
    public static Date getYearFirst(int year){
        Calendar calendar = Calendar.getInstance();
        calendar.clear();
        calendar.set(Calendar.YEAR, year);
        return calendar.getTime();
    }

    /**
     * 获取某年最后一天日期
     * @param year 年份
     * @return Date
     */
    public static Date getYearLast(int year){
        Calendar calendar = Calendar.getInstance();
        calendar.clear();
        calendar.set(Calendar.YEAR, year);
        calendar.roll(Calendar.DAY_OF_YEAR, -1);
        return calendar.getTime();
    }

    /**
     * 获取当前年
     * @return int
     */
    public static int getCurrentYear(){
        Calendar cal = Calendar.getInstance();
        return cal.get(Calendar.YEAR);
    }

    /**
     * 获取当前月
     * @return int
     */
    public static int getCurrentMonth(){
        Calendar cal = Calendar.getInstance();
        return cal.get(Calendar.MONTH )+1;
    }

    /**
     * 返回添加年份 的 年份
     * @return int
     */
    public static int getCurrentYear(int year){
        Calendar cal = Calendar.getInstance();
        cal.add(Calendar.YEAR, year);
        return cal.get(Calendar.YEAR);
    }

    /**
     * 返回添加月份 的 月份
     * @return int
     */
    public static int getCurrentMonth(int month){
        Calendar cal = Calendar.getInstance();
        cal.add(Calendar.MONTH, month);
        return cal.get(Calendar.MONTH )+1;
    }

    /**
     * 计算两个日期相差的年数,不足一年的按0年算
     * @param startDate
     * @param endDate
     * @param format
     * @return int
     */
    public static int countYears(String startDate, String endDate, String format){
        SimpleDateFormat sf = new SimpleDateFormat(format) ;
        try{
            Date sDate = sf.parse(startDate) ;
            Date eDate = sf.parse(endDate) ;

            double years = (Math.abs(eDate.getTime()-sDate.getTime())/(double)(24*3600*1000))/365;
            return (int) years ;
        }catch (ParseException e){
            return 0;
        }
    }

    /**
     * 计算两个日期相差的年数
     * 不足一年的按1年算
     * @param startDate
     * @param endDate
     * @param format
     * @return int
     */
    public static int countYears2(String startDate, String endDate, String format){
        SimpleDateFormat sf = new SimpleDateFormat(format) ;
        try{
            Date sDate = sf.parse(startDate) ;
            Date eDate = sf.parse(endDate) ;

            double years = (Math.abs(eDate.getTime()-sDate.getTime())/(double)(24*3600*1000))/365;
            if((years > (int)years)) {
                return ((int) years) + 1;
            }
            else {
                return (int) years;
            }
        }catch (ParseException e){
            return 0;
        }
    }

    /**
     * 计算两个时间相差的天数
     * 两个时间格式要一样
     * @param startDate
     * @param endDate
     * @param format
     * @return int
     */
    public static int getDiscrepantDays(String startDate, String endDate, String format) {
        SimpleDateFormat sf = new SimpleDateFormat(format) ;
        try{
            Date sDate = sf.parse(startDate) ;
            Date eDate = sf.parse(endDate) ;

            return (int) ((eDate.getTime() - sDate.getTime()) / 1000 / 60 / 60 / 24);
        }catch (ParseException e){
            return 0;
        }
    }

    /**
     * 增加年份
     * @param s
     * @param n
     * @param format
     * @return
     */
    public static String addYear(String s, int n, String format) {
        try {
            SimpleDateFormat sdf = new SimpleDateFormat(format);
            Calendar cd = Calendar.getInstance();
            cd.setTime(sdf.parse(s));
            cd.add(Calendar.YEAR, n);//增加一年
            return sdf.format(cd.getTime());
        } catch (Exception e) {
            logger.info("addYear error : {}", e);
            return null;
        }
    }

    /**
     * 增加月份
     * @param s
     * @param n
     * @param format
     * @return
     */
    public static String addMonth(String s, int n, String format) {
        try {
            SimpleDateFormat sdf = new SimpleDateFormat(format);
            Calendar cd = Calendar.getInstance();
            cd.setTime(sdf.parse(s));
            cd.add(Calendar.MONTH, n);//增加一个月
            return sdf.format(cd.getTime());
        } catch (Exception e) {
            logger.info("addMonth error : {}", e);
            return null;
        }
    }

    /**
     * 增加天数
     * @param s
     * @param n
     * @param format
     * @return
     */
    public static String addDate(String s, int n, String format) {
        try {
            SimpleDateFormat sdf = new SimpleDateFormat(format);
            Calendar cd = Calendar.getInstance();
            cd.setTime(sdf.parse(s));
            cd.add(Calendar.DATE, n);//增加一天
            return sdf.format(cd.getTime());
        } catch (Exception e) {
            logger.info("addDate error : {}", e);
            return null;
        }
    }

    /**
     * 计算周年
     * @param enter_dt
     * @param format
     * @return int
     */
    public static int countAnnualYears(Date enter_dt, String format){
        String sDate = formatDate(enter_dt,format);
        String eDate = formatDate(new Date(),format);
        return countYears(sDate,eDate,format);
    }

    /**
     * 距离下一个周年还有多少天
     * @param enter_dt
     * @param format
     * @return int
     */
    public static int countNextAnnualYearDays(Date enter_dt, String format){
        Date thisDt = new Date();
        SimpleDateFormat sdf = new SimpleDateFormat(format);
        int cay = countAnnualYears(enter_dt,format);
        String ay = addYear(sdf.format(enter_dt),cay+1,format);
        return getDiscrepantDays(sdf.format(thisDt),ay,format);
    }

    /**
     * 距离下一个生日还有多少天
     * @param enter_dt
     * @param format
     * @return int
     */
    public static int countNextBtdDays(Date enter_dt, String format){
        try{
            Date thisDt = new Date();
            SimpleDateFormat sdf = new SimpleDateFormat(format);

            int cay = countAnnualYears(enter_dt,format);
            String ay = addYear(sdf.format(enter_dt),cay,format);
            if(null != ay  && ay.equals(sdf.format(thisDt))) {
                return 0;
            }
            String startDate;
            String endDate;
            startDate = sdf.format(thisDt);
            if(thisDt.after(sdf.parse(ay))){
                endDate = addYear(ay,1,format);
            }else{
                endDate = ay;
            }
            return getDiscrepantDays(startDate,endDate,format);
        }catch(ParseException e){
            return 0;
        }

    }

    /**
     * 第一次生日
     */
    public static String getFirstBtd(Date birthDt,Date enter_dt,String format){
        SimpleDateFormat sdf = new SimpleDateFormat(format);
        Calendar cd = Calendar.getInstance();
        cd.setTime(birthDt);
        int bYear = cd.get(Calendar.YEAR);
        cd.setTime(enter_dt);
        int eYear = cd.get(Calendar.YEAR);

        Calendar cl = Calendar.getInstance();
        cl.setTime(birthDt);
        cl.add(Calendar.YEAR, eYear - bYear);

        String fbd = sdf.format(cl.getTime());

        DateFormat df = new SimpleDateFormat(format);
        String result = "";
        try{
            Date firstBtd = df.parse(fbd);
            if(firstBtd.after(enter_dt)) {
                result = fbd;
            } else {
                result = addYear(fbd,1,format);
            }

            Date now = new Date();
            if(df.parse(result).after(now)) {
                result = "";
            }

        }catch(ParseException e){
            return "";
        }

        return result;
    }

    /**
     * 计算生日总数
     */
    public static int countBirths(Date birthDt,Date enter_dt,String format){
        String firstBtd = getFirstBtd(birthDt,enter_dt,format);
        if(StringUtils.isBlank(firstBtd)){
            return 0;
        }
        int result = 0;
        SimpleDateFormat sdf = new SimpleDateFormat(format);
        Date now = new Date();
        result = countYears2(firstBtd,sdf.format(now),format);
        return result;
    }

    /**
     * 计算两个日期相差多少年多少月多少天
     * @param d1 小日期
     * @param d2 大日期
     * @return
     */
    public static Map<String, Integer> diffDateYearAndMonth(Date d1, Date d2) {
        Map<String, Integer> map = new HashMap<>();
        Calendar c1 = Calendar.getInstance();
        c1.setTime(d1);
        Calendar c2 = Calendar.getInstance();
        c2.setTime(d2);
        int year = 0;
        int month = 0;
        int day = 0;
        if(d2.getTime() > d1.getTime()) {
            year = c2.get(Calendar.YEAR) - c1.get(Calendar.YEAR);
            month = c2.get(Calendar.MONTH) - c1.get(Calendar.MONTH);
            if (month < 0) {
                year = year - 1;
                month = c2.get(Calendar.MONTH) + (12 - c1.get(Calendar.MONTH));
            }
            int dc1 = c1.get(Calendar.DATE);
            int dc2 = c2.get(Calendar.DATE);
            Calendar c3 = Calendar.getInstance();
            c3.setTime(d2);
            c3.add(Calendar.MONTH, -1);//减少一个月
            int maxD1 = c3.getActualMaximum(Calendar.DATE);
            day = dc2 - dc1;
            if (dc2 < dc1) {
                month = month - 1;
                if (month < 0) {
                    year = year - 1;
                    month = 12 + month;
                }
                day = (maxD1 - dc1) + dc2;
            }
        }else{
            long diffDate = (d2.getTime() - d1.getTime()) / (24 * 60 * 60 * 1000);
            diffDate = Math.abs(diffDate);

            if(diffDate > 0) {
                year = (int)(diffDate / 365);
                month =  (int)(diffDate % 365 / 30);
                day = (int)(diffDate % 365 % 30);

                if (year > 0) {
                    year *= -1;
                } else if (month > 0){
                    month *= -1;
                } else {
                    day *= -1;
                }
            }

        }
        map.put("year", year);
        map.put("month", month);
        map.put("day", day);
        return map;
    }

    /**
     * 当前时间转string
     * @return
     */
    public static String getNowDateToStr() {
        return formatDate(new Date(), PATTERN_CLASSICAL);
    }

    /**
     * 从Date变量中提取年份
     * @param date
     * @return
     */
    public static Integer getYear(Date date) {
        Calendar cal = Calendar.getInstance();
        cal.setTime(date);
        return cal.get(Calendar.YEAR);
    }

    /**
     * 从Date变量中提取月份
     * @param date
     * @return
     */
    public static Integer getMonth(Date date) {
        Calendar cal = Calendar.getInstance();
        cal.setTime(date);
        return cal.get(Calendar.MONTH) + 1;
    }

    /**
     * 从Date变量中提取日期（月份中的日期）
     * @param date
     * @return
     */
    public static Integer getDayOfMonth(Date date) {
        Calendar cal = Calendar.getInstance();
        cal.setTime(date);
        return cal.get(Calendar.DATE);
    }

    /**
     * 将字符串转为默认格式Date
     * @param date
     * @return
     */
    public static Date parseDate(String date) {
        try {
            return StringUtils.isBlank(date) ? null : new SimpleDateFormat(DEFAULT_FORMAT).parse(date);
        } catch (ParseException e) {
            return null;
        }
    }

    /**
     * 将格式为“2017/10/11”的日期转换为“20171011”
     * @param date
     * @return
     */
    public static String simpleDateStr(String date) {
        if (StringUtils.isNotBlank(date)) {
            return date.replace("/", "");
        } else {
            return "";
        }
    }

    /**
     * 格式化日期
     * @param date
     * @param format
     * @return
     */
    public static String formatDate(Timestamp date, String format){
        if(StringUtils.isBlank(format)) {
            format = DEFAULT_FORMAT;
        }
        if (date != null) {
            return new SimpleDateFormat(format).format(date);
        }
        return "";
    }

    /**
     * 获取指定年月的第一天
     * @param year
     * @param month
     * @return
     */
    public static String getFirstDayOfMonth(int year, int month) {
        Calendar cal = Calendar.getInstance();
        //设置年份
        cal.set(Calendar.YEAR, year);
        //设置月份
        cal.set(Calendar.MONTH, month-1);
        //获取某月最小天数
        int firstDay = cal.getMinimum(Calendar.DATE);
        //设置日历中月份的最小天数
        cal.set(Calendar.DAY_OF_MONTH,firstDay);
        //格式化日期
        SimpleDateFormat sdf = new SimpleDateFormat(PATTERN_CLASSICAL);
        return sdf.format(cal.getTime());
    }

    /**
     * 获取指定年月的最后一天
     * @param year
     * @param month
     * @return
     */
    public static String getLastDayOfMonth(int year, int month) {
        Calendar cal = Calendar.getInstance();
        //设置年份
        cal.set(Calendar.YEAR, year);
        //设置月份
        cal.set(Calendar.MONTH, month-1);
        //获取某月最大天数
        int firstDay = cal.getActualMaximum(Calendar.DATE);
        //设置日历中月份的最小天数
        cal.set(Calendar.DAY_OF_MONTH,firstDay);
        //格式化日期
        SimpleDateFormat sdf = new SimpleDateFormat(PATTERN_CLASSICAL);
        return sdf.format(cal.getTime());
    }

    /**
     <<<<<<< Temporary merge branch 1
     * 根据指定的日期字符串获取星期几
     * @param strDate 指定的日期字符串(yyyy-MM-dd 或 yyyy/MM/dd)
     * @return week
     *         星期几(MONDAY,TUESDAY,WEDNESDAY,THURSDAY,FRIDAY,SATURDAY,SUNDAY)
     */
    public static int getWeekByDateStr(String strDate)
    {
        int year = Integer.parseInt(strDate.substring(0, 4));
        int month = Integer.parseInt(strDate.substring(5, 7));
        int day = Integer.parseInt(strDate.substring(8, 10));

        Calendar c = Calendar.getInstance();

        c.set(Calendar.YEAR, year);
        c.set(Calendar.MONTH, month - 1);
        c.set(Calendar.DAY_OF_MONTH, day);

        int weekIndex = c.get(Calendar.DAY_OF_WEEK);
        return (weekIndex-1)==0?7:(weekIndex-1);
    }

    /**
     }

     /**
     * 判断当前时间是否在[startTime, endTime]区间，注意时间格式要一致
     *
     * @param nowTime 当前时间
     * @param startTime 开始时间
     * @param endTime 结束时间
     * @return
     * @author jqlin
     */
    public static boolean isEffectiveDate(Date nowTime, Date startTime, Date endTime) {
        if (nowTime.getTime() == startTime.getTime()
                || nowTime.getTime() == endTime.getTime()) {
            return true;
        }

        Calendar date = Calendar.getInstance();
        date.setTime(nowTime);

        Calendar begin = Calendar.getInstance();
        begin.setTime(startTime);

        Calendar end = Calendar.getInstance();
        end.setTime(endTime);

        if (date.after(begin) && date.before(end)) {
            return true;
        } else {
            return false;
        }
    }

    public static Date str2Date(String dateStr, String pattern) {
        try {
            DateFormat parser = new SimpleDateFormat(pattern);
            return parser.parse(dateStr);
        } catch (ParseException ex) {
            return null;
        }
    }

    public static boolean isDate(String strDate) {
        Matcher m = DATE_PATTERN.matcher(strDate);
        boolean blResult;
        if (m.matches()) {
            blResult= true;
        } else {
            blResult= false;
        }
        return blResult;
    }

    public static boolean validValueCode(String valueCode){
        String dimension = "DD";
        String startYear="";
        String startMonth="";
        String startDay="";
        String endYear="";
        String endMonth="";
        String endDay="";
        boolean blResult;

        Matcher m1 = DATE_PATTERN1.matcher(valueCode);
        Matcher m2 = DATE_PATTERN2.matcher(valueCode);
        Matcher m3 = DATE_PATTERN3.matcher(valueCode);
        Matcher m4 = DATE_PATTERN4.matcher(valueCode);

        if(m1.matches()){
            String[] yearPatternRange = valueCode.split("-");
            startYear = yearPatternRange[0];
            if(yearPatternRange.length>1){
                endYear = yearPatternRange[1];
            }
        }else if(m2.matches()){
            String[] monthPatternRange = valueCode.split("-");
            startYear = monthPatternRange[0].substring(0,4);
            startMonth = monthPatternRange[0].substring(4,6);
            if(monthPatternRange.length>1){
                endYear = monthPatternRange[1].substring(0,4);
                endMonth = monthPatternRange[1].substring(4,6);
            }
        }else if(m3.matches()){
            String[] dayPatternRange = valueCode.split("-");
            startYear = dayPatternRange[0].substring(0,4);
            startMonth = dayPatternRange[0].substring(4,6);
            startDay = dayPatternRange[0].substring(6,8);
            if(dayPatternRange.length>1){
                endYear = dayPatternRange[1].substring(0,4);
                endMonth = dayPatternRange[1].substring(4,6);
                endDay = dayPatternRange[1].substring(6,8);
            }
        }else if(m4.matches()){
            startYear = valueCode.substring(0,4);
            endYear = valueCode.substring(0,4);
            String quarterFlag = valueCode.substring(4,5).toUpperCase();
            if("A".equals(quarterFlag)){
                startMonth = "1";
                endMonth = "3";
            }else if("B".equals(quarterFlag)){
                startMonth = "4";
                endMonth = "6";
            }else if("C".equals(quarterFlag)){
                startMonth = "7";
                endMonth = "9";
            }else{
                startMonth = "10";
                endMonth = "12";
            }
        }else if(valueCode.startsWith("last")){
            String rangeNum = valueCode.replace("last","");
            Date nowDate = new Date();
            String sf = "yyyy";
            if("YY".equals(dimension)){
                endYear = getYear(nowDate).toString();
                startYear = addYear(DateUtil.formatDate(nowDate,sf),-Integer.parseInt(rangeNum),sf);
            }else if("MM".equals(dimension)){
                sf = "yyyy-MM";
                String startDate = addMonth(DateUtil.formatDate(nowDate,sf),-Integer.parseInt(rangeNum),sf);
                endYear = getYear(nowDate).toString();
                endMonth = getMonth(nowDate).toString();
                if(null != startDate) {
                    startYear = startDate.split("-")[0];
                    startMonth = startDate.split("-")[1];
                }
            }else if("DD".equals(dimension)){
                sf = DEFAULT_FORMAT;
                String startDate = addMonth(DateUtil.formatDate(nowDate,sf),-Integer.parseInt(rangeNum),sf);
                endYear = getYear(nowDate).toString();
                endMonth = getMonth(nowDate).toString();
                endDay = getDayOfMonth(nowDate).toString();
                if(null != startDate) {
                    startYear = startDate.split("-")[0];
                    startMonth = startDate.split("-")[1];
                    startDay = startDate.split("-")[2];
                }
            }
        }


        if (m1.matches() || m2.matches() || m3.matches() || valueCode.startsWith("last")) {
            blResult= true;
        } else {
            blResult= false;
        }
        return blResult;
    }

    /**
     * 测试主方法
     * @param args
     */
    public static void main(String[] args) {
//        validValueCode("2019-2020");
//        validValueCode("2019D");
//        validValueCode("20190833-");
//        validValueCode("last13");

        int currentYear = getCurrentYear();
        System.out.println(currentYear);
        int currentMonth = getCurrentMonth();
        System.out.println(currentMonth);
        int currentYear1 = getCurrentYear(-1);
        System.out.println(currentYear1);
        int currentMonth1 = getCurrentMonth(-12);
        System.out.println(currentMonth1);
    }

    /**
     * 根据生日判断星座
     * @param month
     * @param day
     * @return
     */
    public static String getConstellation(int month, int day) {
        return day < dayArr[month - 1] ? constellationArr[month - 1]
                : constellationArr[month];
    }
}
